#!BPY

"""
Name: 'WebGL with Material (.js)'
Blender: 244
Group: 'Export'
Tooltip: 'WebGL exporter'
"""
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2010 tmasda 
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

__author__ = "tmasda"
__url__ = ("https://sites.google.com/site/hackthewebgl/")
__version__ = "0.30"

__bpydoc__ = """
Blender Exporter for WebGL.
"""
import Blender
import bpy
import re
from Blender import *
from Blender.BGL import *
from Blender.Mathutils import *

EVENT_DRAW = 1
EVENT_FILEBROWS = 2
EVENT_EXPORT = 3
EVENT_EXIT = 4

fileName = ""
btnExportIndex = Draw.Create(1)
btnTargetMenu = Draw.Create(1)
btnPreciseMenu = Draw.Create(0)
btnNormPut = Draw.Create(1)
btnDiffPut = Draw.Create(0)
btnSpecPut = Draw.Create(0)
btnVertColor = Draw.Create(1)
btnCnvYup = Draw.Create(0)
btnFilename = Draw.Create("")
btnObjectName = Draw.Create("BlenderModel")

priciseFormat = ("%.2f","%.3f","%.4f")

def debug(str):
	#return str
	print str

def getObjectName(object):
	newName = object.name.replace("@", "at_")
	newName = re.sub("\.([0-9]+?)",r"_\1",newName)

	return newName
	

def compressStr(str):
	str = re.sub("\.0+,",r",",str)
	return re.sub("\.([1-9]+?)0+",r".\1",str)



def getObjectTexture(object):
	texName = ""
	if (object.type != "Mesh"):
		return texName 
	mesh = Mesh.New()
	mesh.getFromObject(object.name)
	texList = []
	if (mesh.faceUV):
		for f in mesh.faces:
			if (f.image not in texList):
				texList.append(f.image)

		if (len(texList) > 1):
			debug("Error:" + object.name + "use some texture. count=" + str(len(texList)))
			return texName

		splpath = texList[0].filename.split("\\")
		texName = splpath[len(splpath)-1]+ '";\n'
	return texName

def checkFace(face, texIdx, curTexFile):
	if texIdx == -1:
		if not face.image:
			return True
	else:
		if face.image and face.image.filename == curTexFile:
			return True
	return False

def exportObject(file, object, texArray):
	if (object.type != "Mesh"):
		return True
	mesh = Mesh.New()
	mesh.getFromObject(object.name)
	debug("number of faces:" + str(len(mesh.faces)))
	debug("number of vertices:" + str(len(mesh.verts)))
	# check face's vertex count
	for f in mesh.faces:
		if len(f.v) != 3:
			print "Error:This face has %d verts!" % len(f.v)
			return False

	useTex = False
	if mesh.faceUV:
		for f in mesh.faces:
			if f.mode & Mesh.FaceModes["TEX"]:
				useTex = True
				break

	yup = btnCnvYup.val
	debug("faceUV:"+str(mesh.faceUV)+" vertUV:"+str(mesh.vertexUV)+
			" vertexColors:"+str(mesh.vertexColors) + " useTex:"+str(useTex))

	format2float = ""
	format3float = ""
	format4float = ""
	format6float = ""
	format9float = ""
	format12float = ""
	for i in range(2):
		format2float += priciseFormat[btnPreciseMenu.val] + ","
	for i in range(3):
		format3float += priciseFormat[btnPreciseMenu.val] + ","
	for i in range(4):
		format4float += priciseFormat[btnPreciseMenu.val] + ","
	for i in range(6):
		format6float += priciseFormat[btnPreciseMenu.val] + ","
	for i in range(9):
		format9float += priciseFormat[btnPreciseMenu.val] + ","
	for i in range(12):
		format12float += priciseFormat[btnPreciseMenu.val] + ","
	
	# texture
	texIndexList = [-1] * len(mesh.faces)
	if useTex:
		i = 0
		for f in mesh.faces:
			if f.image:
				try:
					idx = texArray.index(f.image.filename)
					texIndexList[i] = idx
				except ValueError:
					texArray.append(f.image.filename)
					texIndexList[i] = (len(texArray)-1);
			i += 1
	texSet = set(texIndexList)
	
	loopIdx =  -1
	for texIdx in texSet:
		loopIdx += 1

		useVertColor = btnVertColor.val
		if useVertColor and not mesh.vertexColors:
			useVertColor = 0

		if useVertColor == 1:
			if texIdx >= 0:
				useVertColor = 0

		if loopIdx == 0:
			curName = btnObjectName.val + "." + getObjectName(object)
			# matrix(local)
			strMatrix = curName + ".matrix = ["
			if yup:
				for i in range(3):
					strMatrix += format4float % (object.matrixLocal[i][0], object.matrixLocal[i][1],
												object.matrixLocal[i][2], object.matrixLocal[i][3],)
				strMatrix += format4float % (object.matrixLocal[3][0], object.matrixLocal[3][2],
											-object.matrixLocal[3][1], object.matrixLocal[3][3],)
			else:
				for i in range(4):
					strMatrix += format4float % (object.matrixLocal[i][0], object.matrixLocal[i][1],
												object.matrixLocal[i][2], object.matrixLocal[i][3],)
		else:
			curName = btnObjectName.val + "." + getObjectName(object) + "_%d" % (loopIdx)

		# number of vertex
		file.write(curName + " = {};\n")

		strTex = curName + '.texName = "'
		strVertex = curName + ".vertices = ["
		strUV = curName + ".texCoords = ["
		strNorm = curName + ".normals = ["
		strDiff = curName + ".diffuses = ["
		strSpec = curName + ".speculars = ["
		strIndex = curName + ".indices = ["
		strVertColor = curName + ".vertColors = ["
		strTexIndex = curName + ".texIndices = ["

		curTexFile = ""
		if texIdx != -1:
			curTexFile = texArray[texIdx]
			splpath = curTexFile.split("\\")
			texName = splpath[len(splpath)-1]
			strTex += texName + '";\n'

		curFaces = []
		for f in mesh.faces:
			if useTex:
				if checkFace(f, texIdx, curTexFile):
					curFaces.append(f)
			else:
				curFaces.append(f)
		file.write(curName + ".numItem = %i;\n" %(len(curFaces) * 3))

		# use index list
		if (btnExportIndex.val):
			verts = mesh.verts
			vList = [];
			idxList = [];

			numVListItem = 0
			for f in curFaces:
				v = []
				idx = [-1] * 3
				for i in range(3):
					if useTex:
						if mesh.vertexColors:
							v.append((f.verts[i].co, f.verts[i].no, f.uv[i], f.col[i]))
						else:
							v.append((f.verts[i].co, f.verts[i].no, f.uv[i]))
					else:
						if mesh.vertexColors:
							v.append((f.verts[i].co, f.verts[i].no, f.col[i]))
						else:
							v.append((f.verts[i].co, f.verts[i].no))

					try:
						idx[i] = vList.index(v[i])
					except ValueError:
						vList.append(v[i])
						idx[i] = numVListItem
						numVListItem += 1
				idxList.append((idx[0], idx[1], idx[2]))
						

			for v in vList:
				# uv
				if useTex:
					colIdx = 3
					strUV += format2float % (v[2][0], v[2][1])
				else:
					colIdx = 2
			
			if not yup:
				for v in vList:
					# vertex
					strVertex += format3float % (v[0].x, v[0].y, v[0].z)
					if (btnNormPut.val):
						# norm 
						strNorm += format3float % (v[1].x, v[1].y, v[1].z)
			else:
				for v in vList:
					# vertex
					strVertex += format3float % (v[0].x, v[0].z, -v[0].y)
					if (btnNormPut.val):
						# norm 
						strNorm += format3float % (v[1].x, v[1].z, -v[1].y)

			if useVertColor:
				for v in vList:
					strVertColor += format4float % (v[colIdx].r/255.0,v[colIdx].g/255.0,v[colIdx].b/255.0,v[colIdx].a/255.0)
			for i in idxList:
				strIndex += "%d,%d,%d," % (i[0], i[1], i[2])
		else:
			# uv
			if useTex:
				for f in curFaces:
					for uv in f.uv:
						strUV += format2float % (uv[0], uv[1])
			if not yup:
				# vertex
				for f in curFaces:
					for v in f.verts:
						strVertex += format3float % (v.co.x, v.co.y, v.co.z)
				# norm
				if (btnNormPut.val):
					for f in curFaces:
						for v in f.verts:
							strNorm += format3float % (v.no.x, v.no.y, v.no.z)
			else :
				# vertex
				for f in curFaces:
					for v in f.verts:
						strVertex += format3float % (v.co.x, v.co.z, -v.co.y)
				# norm
				if (btnNormPut.val):
					for f in curFaces:
						for v in f.verts:
							strNorm += format3float % (v.no.x, v.no.z, -v.no.y)
			if useVertColor:
				for f in curFaces:
						for col in f.col:
							strVertColor += format4float % (col.r/255.0,col.g/255.0,col.b/255.0,col.a/255.0)
			

		# diffuse
		matlist = []
		matNo = -1
		if (mesh.materials != [] and (btnDiffPut.val or btnSpecPut.val)):
			for f in curFaces:
				if (f.mat not in matlist):
					matlist.append(f.mat)
			if (len(matlist) > 1):
				#debug("Error:" + object.name + "use some materials. count=" + str(len(matlist)))
				debug("Warning:" + object.name + " use some materials. count=" + str(len(matlist)))
				#return False
			matNo = matlist[0]
			#debug("material No:" + str(matNo))

		if (matNo != -1):
			mtr = Material.Get(mesh.materials[matNo].name)
			#debug("material name:" + str(mtr[matNo].name))
			debug("material name:" + str(mesh.materials[matNo].name))
			if (btnDiffPut.val):
				strDiff += format4float % (mtr.R, mtr.G, mtr.B, mtr.alpha)

			# specular
			if (btnSpecPut.val):
				strSpec += format4float % (mtr.specR, mtr.specG, mtr.specB, mtr.spec)

		# add to file
		strMatrix = compressStr(strMatrix)
		file.write(strMatrix[:-1]+ "];\n")
		strVertex = compressStr(strVertex)
		file.write(strVertex[:-1]+ "];\n")
		#if mesh.faceUV:
		if useTex:
			if texIdx != -1:
				file.write(strTex)

			strUV = compressStr(strUV)
			file.write(strUV[:-1] + "];\n")
		if (btnNormPut.val):
			strNorm = compressStr(strNorm)
			file.write(strNorm[:-1] + "];\n")
		if (matNo != -1):
			if (btnDiffPut.val and matNo != -1):
				strDiff = compressStr(strDiff)
				file.write(strDiff[:-1] + "];\n")
			if (btnSpecPut.val):
				strSpec = compressStr(strSpec)
				file.write(strSpec[:-1] + "];\n")
		if useVertColor:
			strVertColor = compressStr(strVertColor)
			file.write(strVertColor[:-1] + "];\n")
		if (btnExportIndex.val):
			file.write(strIndex[:-1] + "];\n")
	
	if len(texSet) > 1:
		curName = btnObjectName.val + "." + getObjectName(object)
		strChild = curName + ".child = ["
		for i in range(1,len(texSet) ):
			strChild += curName + "_%d," % (i)

		file.write(strChild[:-1] + "];\n")
	return True

def event(evt, val): 
	if (evt == Draw.QKEY and not val):
		Draw.Exit()

def button_event(evt):
	global targetNo, fileName, declarTopFlag, normPutFlag, diffPutFlag, specPutFlag

	if (evt == EVENT_EXIT):
		Draw.Exit()
		return
	elif (evt == EVENT_FILEBROWS):
		Window.FileSelector(FileSelected, "Export .js", btnFilename.val)
	elif (evt == EVENT_EXPORT and btnFilename.val != ""):
		out = file(btnFilename.val,"w")
		sce = bpy.data.scenes.active;
		debug("scene name:" + sce.name);
		out.write(btnObjectName.val + " = {};\n")
		# Zup or Yup info
		if (btnCnvYup.val):
			out.write(btnObjectName.val + ".isYup = 1;\n")
		#else:
		#	out.write(btnObjectName.val + ".isYup = 0;\n")

		ret = False
		texArray = []
		if (btnTargetMenu.val == 0):
			obj = sce.objects.active
			debug("== " + obj.name + " (" + obj.type + ") ==")
			ret = exportObject(out, obj, texArray)
		else:
			childList = {}
			for obj in sce.objects:
				debug("== " + obj.name + " (" + obj.type + ") ==")
				if (obj.type == "Mesh"):
					childList[obj.name] = []
					ret = exportObject(out, obj,texArray)
					if not ret:
						break;
				elif (obj.type == "Armature"):
					childList[obj.name] = []
			if ret:
				for obj in sce.objects:
					if (obj.type == "Mesh" and obj.parent):
						debug("parent=" + obj.parent.name + ";type=" + obj.parent.type)
						childList[obj.parent.name].append(getObjectName(obj))
						debug(obj.name + " is " + obj.parent.name + "'s child")
				for obj in sce.objects:
					if obj.type == "Mesh" or obj.type == "Armature":
						if childList[obj.name] and len(childList[obj.name]) > 0:
							curName = btnObjectName.val + "." + getObjectName(obj)
							if obj.type == "Armature":
								out.write(curName + " = {};\n");
								out.write(curName + ".isArmature = 1;\n");
							childStr = curName + ".child = ["
							for child in childList[obj.name]:
								childStr += btnObjectName.val + "." + child + ","
							out.write(childStr[:-1] + "];\n");
							
		# Texture List
		if (ret):

			texListStr = btnObjectName.val + ".texList = ["
			for t in texArray:
				splpath = t.split("\\")
				texName = splpath[len(splpath)-1]
				texListStr += '"' + texName + '",'
			if (len(texArray)):
				out.write(texListStr[:-1] + "];\n");
			else:
				out.write(texListStr + "];\n");

		out.close()

		if (ret):
			Draw.PupMenu("Export Successful!")
		else:
			Draw.PupMenu("Export Fail!")

		return

	Draw.Redraw(1)

def FileSelected(filename):
	global btnFilename 
	if (filename != ""):
		btnFilename.val = filename


def draw():
	global btnTargetMenu, btnPreciseMenu, btnObjectName, btnNormPut, btnDiffPut, btnSpecPut, btnFilename
	global btnCnvYup, btnExportIndex, btnVertColor 

	glClear(GL_COLOR_BUFFER_BIT)
	startYpos = 320;
	ofsY = 30;
	btnExportIndex = Draw.Toggle('Export Use Index List', EVENT_DRAW, 10, startYpos, 200, 20, btnExportIndex.val);
	startYpos -= ofsY;
	btnTargetMenu = Draw.Menu("Output Target%t|Active Object%x0|All Object%x1", EVENT_DRAW, 10, startYpos, 200, 20, btnTargetMenu.val)
	startYpos -= ofsY;
	btnPreciseMenu = Draw.Menu("Quality%t|low%x0|middle%x1|high%x2", EVENT_DRAW, 10, startYpos, 200, 20, btnPreciseMenu.val, "higher quality , bigger output size")
	startYpos -= ofsY;
	btnObjectName = Draw.String("Object Name:", EVENT_DRAW, 10, startYpos, 200, 20, btnObjectName.val, 260);
	startYpos -= ofsY;
	btnNormPut = Draw.Toggle('Export Normals', EVENT_DRAW, 10, startYpos, 200, 20, btnNormPut.val);
	startYpos -= ofsY;
	btnDiffPut = Draw.Toggle('Export Material Diffuse', EVENT_DRAW, 10, startYpos, 200, 20, btnDiffPut.val,"RGBA");
	startYpos -= ofsY;
	btnSpecPut = Draw.Toggle('Export Material Specular', EVENT_DRAW, 10, startYpos, 200, 20, btnSpecPut.val,"RGB + Specularity");
	startYpos -= ofsY;
	btnVertColor = Draw.Menu("Export Vertex Color%t|Never%x0|Only when it doesn't have texture.%x1|Always if it has vertex color.%x2", EVENT_DRAW, 10, startYpos, 200, 20, btnVertColor.val)
	startYpos -= ofsY;
	btnCnvYup = Draw.Toggle('Convert Zup to Yup', EVENT_DRAW, 10, startYpos, 200, 20, btnCnvYup.val);
	startYpos -= ofsY;
	
	btnFilename = Draw.String('Export Filepath:', EVENT_DRAW, 10, startYpos, 280, 20, btnFilename.val, 260 )
	Draw.PushButton('...', EVENT_FILEBROWS, 300, startYpos, 20, 20,'brows file');
	startYpos -= ofsY;
	Draw.PushButton("Export", EVENT_EXPORT, 10, startYpos, 150, 20)
	Draw.PushButton("Exit", EVENT_EXIT, 170, startYpos, 150, 20)


Draw.Register(draw, event, button_event)
